/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.icanci.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.icanci.basic.commonutils.R;
import cn.icanci.basic.exception.ApplicationException;
import cn.icanci.order.dic.OrderStatus;
import cn.icanci.order.entity.OrderEntity;
import cn.icanci.order.factory.OrderInfoFactory;
import cn.icanci.order.form.PreOrderForm;
import cn.icanci.order.form.ViewVO;
import cn.icanci.order.mapper.OrderInfoMapper;
import cn.icanci.order.module.OrderInfo;
import cn.icanci.order.service.PreOrderService;
import cn.icanci.order.spi.*;
import cn.icanci.order.spi.dto.CouponDTO;
import cn.icanci.order.spi.dto.TicketDTO;
import cn.icanci.order.spi.dto.UserInfoDTO;
import cn.icanci.order.utils.RegexPatterns;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;

/**
 * TODO 重复单验证
 * @author icanci
 * @date Created in 2021/05/04 10:59
 * @version 1.0
 */
@Service
@Slf4j
public class PreOrderServiceImpl implements PreOrderService {
    @Resource
    private TradeLogService           tradeLogService;
    @Resource
    private UserInfoServiceClient     userInfoServiceClient;
    @Resource
    private TicketServiceClient       ticketServiceClient;
    @Resource
    private CouponServiceClient       couponServiceClient;
    @Resource
    private OrderInfoFactory          orderInfoFactory;
    @Resource
    private OrderInfoMapper           orderInfoMapper;
    @Resource
    private EmailServiceClient        emailServiceClient;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    // ======================================== 接口方法 ========================================
    /**
     * 下单
     * @param preOrderForm
     * @return
     */
    @Override
    public R preBook(PreOrderForm preOrderForm) {
        try {
            tradeLogService.info(log, "预定验证开始，参数信息，preOrderForm:{}", preOrderForm);
            // 请求参数验证
            R requestR = requestVerification(preOrderForm);
            if (!requestR.getSuccess()) {
                return requestR;
            }
            // 用户信息验证
            R userInfoR = userInfoVerification(preOrderForm);
            if (!userInfoR.getSuccess()) {
                return userInfoR;
            }
            // 机票信息验证
            R ticketInfoR = ticketInfoVerification(preOrderForm);
            if (!ticketInfoR.getSuccess()) {
                return ticketInfoR;
            }
            // 优惠券验证
            R couponInfoR = couponInfoVerification(preOrderForm);
            if (!couponInfoR.getSuccess()) {
                return couponInfoR;
            }
            // 生成订单
            R orderInfoR = preBookInfo(preOrderForm);
            return orderInfoR;
        } catch (Exception e) {
            tradeLogService.error(log, "预定失败，错误信息:{}", e.getStackTrace());
            return R.error().message("预定失败");
        } finally {
            tradeLogService.removeAll();
        }
    }

    /**
     * 下单
     * @param orderId
     * @return
     */
    @Override
    public R book(Long orderId) {
        try {
            OrderEntity orderEntity = orderInfoFactory.create(orderId);
            if (orderEntity.getOrderStatus().equals(OrderStatus.CANCEL)) {
                return R.error().message("订单已经取消，支付失败");
            }
            orderEntity.setOrderStatus(OrderStatus.PAID);
            orderEntity.updateOneOrder();
            // 更新操作
            ticketServiceClient.updateTicketDetailDataById(orderEntity.getTicketId());
            // 是否具有优惠券
            if (!StringUtils.isEmpty(orderEntity.getCouponNumber())) {
                couponServiceClient.updateOneCoupon(orderEntity.getCouponNumber(), orderEntity.getUserId());
            }
            emailServiceClient.bookOrderEmail(orderEntity.getEmailAddress(), orderEntity.getOrderSerialNo(), orderEntity.getStartCity(), orderEntity.getArriveCity());
            return R.ok().message("支付成功");
        } catch (ApplicationException e) {
            tradeLogService.error(log, "", e.getMessage());
            return R.error().message("订单不存在");
        }
    }

    /**
     * 用户查询订单
     * @param phoneNumber
     * @return
     */
    @Override
    public R queryManyRowByUserPhone(String phoneNumber) {
        List<OrderInfo> orderInfos = orderInfoMapper.queryManyRowByUserPhone(phoneNumber);
        if (CollectionUtil.isEmpty(orderInfos)) {
            return R.error().message("暂无订单数据");
        }
        return R.ok().message("加载成功").data("orderInfos", orderInfos);
    }

    /**
     * 管理员查询订单
     * @return
     */
    @Override
    public R queryManyRowByAdmin() {
        List<OrderInfo> orderInfos = orderInfoMapper.queryManyRowByAdmin();
        if (CollectionUtil.isEmpty(orderInfos)) {
            return R.error().message("暂无订单数据");
        }
        return R.ok().message("加载成功").data("orderInfos", orderInfos);
    }

    /**
     * 查询
     * @param orderId
     * @return
     */
    @Override
    public R queryOneRowByUserPhone(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.queryOneRowByOrderId(orderId);
        if (null == orderInfo) {
            return R.error().message("暂无订单数据");
        }
        return R.ok().message("加载成功").data("orderInfo", orderInfo);
    }

    /**
     * 取消订单
     * @param orderId
     * @return
     */
    @Override
    public R cancelOrder(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.queryOneRowByOrderId(orderId);
        if (null == orderInfo) {
            return R.error().message("暂无订单数据");
        }
        orderInfo.setOrderStatus(OrderStatus.CANCEL);
        orderInfoMapper.updateOneOrder(orderInfo);
        return R.ok().message("取消成功");
    }

    /**
     * 管理员分析订单
     * @return 返回分析的订单
     */
    @Override
    public R queryManyToAnalysisByAdmin() {
        List<OrderInfo> orderInfos = orderInfoMapper.queryManyRowByAdmin();
        if (null == orderInfos) {
            return R.error().message("暂无订单数据");
        }
        List<ViewVO> viewVOS = Lists.newArrayList();
        Map<String, List<OrderInfo>> collect = orderInfos.stream().collect(Collectors.groupingBy(o -> o.getStartCity() + "-" + o.getArriveCity()));
        for (Map.Entry<String, List<OrderInfo>> entry : collect.entrySet()) {
            ViewVO viewVO = new ViewVO();
            viewVO.setType(entry.getKey());
            viewVO.setValue(entry.getValue().size() * 1.0 / orderInfos.size());
            viewVOS.add(viewVO);
        }
        return R.ok().message("查询成功").data("orderAnalysis", viewVOS);
    }

    // ======================================== 非接口方法 ========================================
    /**
     * 生成订单操作
     * @param preOrderForm
     * @return
     */
    private R preBookInfo(PreOrderForm preOrderForm) {
        try {
            // 直接查询数据
            R userInfoR = userInfoServiceClient.queryOneUserInfo(preOrderForm.getUserId());
            LinkedHashMap userInfoMap = (LinkedHashMap) userInfoR.getData().get("userInfo");
            UserInfoDTO userInfoDTO = objectMapper.convertValue(userInfoMap, UserInfoDTO.class);

            R ticketInfoR = ticketServiceClient.queryTicketDetailDataById(preOrderForm.getFlightNumberId());
            LinkedHashMap ticketInfoMap = (LinkedHashMap) ticketInfoR.getData().get("ticket");
            TicketDTO ticketDTO = objectMapper.convertValue(ticketInfoMap, TicketDTO.class);

            String couponNumber = preOrderForm.getCouponNumber();
            if (!StringUtils.isEmpty(couponNumber)) {
                R couponInfoR = couponServiceClient.queryOneCoupon(preOrderForm.getUserId(), couponNumber);
                LinkedHashMap couponMap = (LinkedHashMap) couponInfoR.getData().get("coupon");
                CouponDTO couponDTO = objectMapper.convertValue(couponMap, CouponDTO.class);
                OrderEntity orderEntity = orderInfoFactory.create(userInfoDTO, ticketDTO, couponDTO);
                // 查询一手
                R isPreBuyed = orderEntity.queryOneBuyed();
                if (!isPreBuyed.getSuccess()) {
                    return isPreBuyed;
                }
                orderEntity.saveOneOrder();
                emailServiceClient.preOrderEmail(orderEntity.getEmailAddress(), orderEntity.getOrderSerialNo(), orderEntity.getStartCity(), orderEntity.getArriveCity());
                return R.ok().message("预定成功，请尽快付款").data("orderInfo", orderEntity);
            }
            OrderEntity orderEntity = orderInfoFactory.create(userInfoDTO, ticketDTO);
            R isPreBuyed = orderEntity.queryOneBuyed();
            if (!isPreBuyed.getSuccess()) {
                return isPreBuyed;
            }
            orderEntity.saveOneOrder();
            emailServiceClient.preOrderEmail(orderEntity.getEmailAddress(), orderEntity.getOrderSerialNo(), orderEntity.getStartCity(), orderEntity.getArriveCity());
            return R.ok().message("预定成功，请尽快付款").data("orderInfo", orderEntity);
        } catch (Exception e) {
            return R.error().message("下单失败。" + e.getMessage());
        }
    }

    /**
     * 请求参数验证
     * @param preOrderForm
     * @return
     */
    private R requestVerification(PreOrderForm preOrderForm) {
        if (null == preOrderForm) {
            return R.error().message("请求失败，参数不合法");
        }
        if (preOrderForm.getUserId() == null || preOrderForm.getUserId() <= 0) {
            return R.error().message("请求失败，用户信息不合法");
        }
        // 优惠券不需要处理
        //        if (StringUtils.isEmpty(preOrderForm.getCouponNumber())) {
        //            return R.error().message("请求失败，优惠券信息不合法");
        //        }
        if (preOrderForm.getFlightNumberId() == null || preOrderForm.getFlightNumberId() <= 0) {
            return R.error().message("请求失败，机票信息不合法");
        }
        return R.ok();
    }

    /**
     * 用户信息验证
     * @param preOrderForm
     * @return
     */
    private R userInfoVerification(PreOrderForm preOrderForm) {
        Integer userId = preOrderForm.getUserId();
        R r = userInfoServiceClient.queryOneUserInfo(userId);
        if (!r.getSuccess()) {
            return r;
        }
        LinkedHashMap linkedHashMap = (LinkedHashMap) r.getData().get("userInfo");
        UserInfoDTO userInfoDTO = objectMapper.convertValue(linkedHashMap, UserInfoDTO.class);
        if (null == userInfoDTO) {
            return R.error().message("用户不存在");
        }
        // 验证姓名和身份证
        // TODO 真实情况需要第三方接口验证通道
        // 验证姓名和身份证是否存在
        if (StringUtils.isEmpty(userInfoDTO.getReallyName())) {
            return R.error().message("用户未绑定身份信息");
        }
        if (StringUtils.isEmpty(userInfoDTO.getIdCardNumber())) {
            return R.error().message("用户未绑定身份信息");
        }
        // TODO 此处暂时只处理18位身份证
        String idCardNumber = userInfoDTO.getIdCardNumber();
        boolean validCard18 = IdcardUtil.isValidCard18(idCardNumber);
        if (!validCard18) {
            return R.error().message("用户身份证信息不合法");
        }
        int ageByIdCard = IdcardUtil.getAgeByIdCard(idCardNumber);
        // 获取年龄
        if (ageByIdCard < 18) {
            return R.error().message("用户未满18周岁，不能单独购票出行");
        }
        // 验证邮箱
        if (StringUtils.isEmpty(userInfoDTO.getEmailAddress())) {
            return R.error().message("用户邮箱信息不能为空");
        }
        // 正则邮箱
        if (!RegexPatterns.isEmail(userInfoDTO.getEmailAddress())) {
            return R.error().message("邮箱格式错误");
        }
        // 正则手机号
        if (!RegexPatterns.isPhone(userInfoDTO.getPhoneNumber())) {
            return R.error().message("手机号码格式错误");
        }
        // 用户信息验证通过
        tradeLogService.info(log, "PreOrderService userInfoVerification,userInfo:{}", userInfoDTO);
        return R.ok().message("用户信息验证通过");
    }

    /**
     * 机票信息验证
     * @param preOrderForm
     * @return
     */
    private R ticketInfoVerification(PreOrderForm preOrderForm) {
        Long flightNumberId = preOrderForm.getFlightNumberId();
        R r = ticketServiceClient.queryTicketDetailDataById(flightNumberId);
        if (!r.getSuccess()) {
            return r;
        }
        LinkedHashMap linkedHashMap = (LinkedHashMap) r.getData().get("ticket");
        TicketDTO ticketDTO = objectMapper.convertValue(linkedHashMap, TicketDTO.class);
        if (null == ticketDTO) {
            return R.error().message("机票信息不正确");
        }
        // 验证机票的状态
        if (ticketDTO.getStatus() == 0) {
            return R.error().message("机票已经售出");
        }
        // 机票信息验证通过
        tradeLogService.info(log, "PreOrderService ticketInfoVerification,ticketDTO:{}", ticketDTO);
        return R.ok().message("机票信息验证通过");
    }

    /**
     * 优惠券验证
     * @param preOrderForm
     * @return
     */
    private R couponInfoVerification(PreOrderForm preOrderForm) {
        String couponNumber = preOrderForm.getCouponNumber();
        if (StringUtils.isEmpty(couponNumber)) {
            return R.ok().message("暂无优惠券");
        }
        R r = couponServiceClient.queryOneCoupon(preOrderForm.getUserId(), couponNumber);
        if (!r.getSuccess()) {
            return r;
        }
        LinkedHashMap linkedHashMapCoupon = (LinkedHashMap) r.getData().get("coupon");
        CouponDTO couponDTO = objectMapper.convertValue(linkedHashMapCoupon, CouponDTO.class);
        // 验证 优惠券的状态
        if (null == couponDTO) {
            return R.error().message("优惠券不存在");
        }
        if (couponDTO.getStatus() != 2) {
            return R.error().message("优惠券不可用");
        }
        boolean start = couponDTO.getEffectiveStartTime().getTime() <= System.currentTimeMillis();
        boolean end = couponDTO.getEffectiveEndTime().getTime() >= System.currentTimeMillis();
        if (!(start && end)) {
            return R.error().message("优惠券已经过期");
        }
        if (couponDTO.getDeleted() == 0) {
            return R.error().message("优惠券不可用");
        }
        // 验证城市和价格
        R rt = ticketServiceClient.queryTicketDetailDataById(preOrderForm.getFlightNumberId());
        LinkedHashMap linkedHashMapTicket = (LinkedHashMap) rt.getData().get("ticket");
        TicketDTO ticketDTO = objectMapper.convertValue(linkedHashMapTicket, TicketDTO.class);
        if (StringUtils.isEmpty(couponDTO.getStartCity()) || StringUtils.isEmpty(couponDTO.getEndCity())) {
            return R.error().message("优惠券已经破碎");
        }
        if (!(couponDTO.getStartCity().equals(ticketDTO.getStartCity()) && couponDTO.getEndCity().equals(ticketDTO.getArriveCity()))) {
            return R.error().message("优惠券起始抵达城市和机票不一致，不可使用");
        }
        if (couponDTO.getFullMoney().doubleValue() > ticketDTO.getPrice().doubleValue()) {
            return R.error().message("优惠券不可用，机票价格小于抵用券使用最低价");
        }
        // 优惠券信息验证通过
        tradeLogService.info(log, "PreOrderService couponInfoVerification,couponDTO:{}", couponDTO);
        return R.ok().message("优惠券信息验证通过");
    }
}
